#include "InputHandle.h"

namespace Herz
{
	InputHandle *InputHandle::instance = nullptr;

	InputHandle *InputHandle::create()
	{
		if (!instance)
			instance = new InputHandle;
		return instance;
	}

	bool InputHandle::initialise(HWND hWnd, const Resolution& resolution)
	{
		mWnd		= hWnd;
		mResolution	= resolution;

		ZeroMemory(&mKeys, sizeof(Byte)*256);
		for (UInt16 i = 0; i < 256; i++)	mNKeys[i] = true;
		ZeroMemory(&mMouse, sizeof(DIMOUSESTATE2));
		for (UInt16 i = 0; i < 8; i++)		mNMouseButtons[i] = true;

		if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&mInput, NULL)))
		{
			MessageBox(NULL, "Cannot create DirectInput", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if(FAILED(mInput->CreateDevice(GUID_SysKeyboard, &mKeyboardInput, NULL)))
		{
			MessageBox(NULL,"Fail at create keyboard device!", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if(FAILED(mKeyboardInput->SetDataFormat(&c_dfDIKeyboard)))
		{
			MessageBox(NULL,"Fail at SetDataFormat on keyboard device", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if(FAILED(mKeyboardInput->SetCooperativeLevel(mWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE )))
		{
			MessageBox(NULL,"Fail at SetCooperativeLevel on keyboard device", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if(FAILED(mInput->CreateDevice(GUID_SysMouse, &mMouseInput, NULL)))
		{
			MessageBox(NULL,"Fail at create mouse device!", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if(FAILED(mMouseInput->SetDataFormat(&c_dfDIMouse2)))
		{
			MessageBox(NULL,"Fail at SetDataFormat on mouse device", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		if(FAILED(mMouseInput->SetCooperativeLevel(mWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND)))
		{
			MessageBox(NULL,"Fail at SetCooperativeLevel on mouse device", "Error", MB_OK | MB_ICONERROR);
			return false;
		}

		return true;
	}

	void InputHandle::destroy()
	{
		if(mKeyboardInput)
			mKeyboardInput->Unacquire();
		if(mMouseInput)
			mMouseInput->Unacquire();

		if(mKeyboardInput)
			mKeyboardInput->Release();
		if(mMouseInput)
			mMouseInput->Release();

		if(mInput)
			mInput->Release();
	}

	void InputHandle::update()
	{
		HRESULT hr;
		hr = mKeyboardInput->GetDeviceState(sizeof(Byte)*256, &mKeys);
		if (FAILED(hr))
		{
			do
			{
				hr = mKeyboardInput->Acquire();
			}
			while (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED);
		}

		hr = mMouseInput->GetDeviceState(sizeof(DIMOUSESTATE2), &mMouse);
		if(FAILED(hr))
		{
			do
			{
				hr = mMouseInput->Acquire();
				mMouseX += mMouse.lX;
				mMouseY += mMouse.lY;

				if (mMouseX < 0)	mMouseX = 0;
				if (mMouseY < 0)	mMouseY = 0;

				if (mMouseX > mResolution.width)	mMouseX = mResolution.width;
				if (mMouseX < mResolution.height)	mMouseX = mResolution.height;
			}
			while (hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED);
		}
	}

	bool InputHandle::isKey(Byte key)
	{
		if (mKeys[key] & 0x80) return true;
		return false;
	}

	bool InputHandle::isNewKey(Byte key)
	{
		if (mKeys[key] & 0x80)
		{
			if (mNKeys[key])
			{
				mNKeys[key] = false;
				return true;
			}
		}
		else
			mNKeys[key] = true;
		return false;
	}

	bool InputHandle::isMouseButton(Byte key)
	{
		if (mMouse.rgbButtons[key] & 0x80) return true;
		return false;
	}

	bool InputHandle::isNewMouseButton(Byte key)
	{
		if (mMouse.rgbButtons[key] & 0x80)
		{
			if (mNMouseButtons[key])
			{
				mNMouseButtons[key] = false;
				return true;
			}
		}
		else
			mNMouseButtons[key] = true;
		return false;
	}
} // namespace Herz